#ifndef GLOBAL_H
#define GLOBAL_H

#include <assert.h>
#include <vector>
#include <stdlib.h>
#include <stdio.h>

#ifndef UNIMPLEMENTED
#define UNIMPLEMENTED
#endif

namespace NITK {
template <typename T> T min(const T &left, const T &right) { return (left < right ? left : right); }
template <typename T> T max(const T &left, const T &right) { return (left > right ? left : right); }

class Color
{
public:
    static int alpha(unsigned int color) { return (color >> 24) & 0xff; }
    static int red(unsigned int color) { return (color >> 16) & 0xff; }
    static int green(unsigned int color) { return (color >> 8) & 0xff; }
    static int blue(unsigned int color) { return color & 0xff; }
    static unsigned int rgba(int red, int green, int blue, int alpha = 255)
        { return ((alpha & 0xff) << 24) | ((red & 0xff) << 16) | ((green & 0xff) << 8) | (blue & 0xff); }
    static void toRgba(unsigned int color, int &r, int &g, int &b, int &a)
    {
        r = red(color);
        g = green(color);
        b = blue(color);
        a = alpha(color);
    }
};

// template <typename T>
// static inline bool removeOne(std::vector<T*> &vector, T *t)
// {
//     for (std::vector<T*>::iterator it = vector.begin(); it != vector.end(); ++it) {
//         if (*it == card) {
//             vector.erase(it);
//             return true;
//         }
//     }
//     return false;
// }


// template <typename T>
// static bool removeOne(std::vector<T> &container, const T &t)
// {
//     std::vector<T>::iterator i = container.begin();
//     for (std::vector<T>::iterator
//              it = container.begin();
//          it != container.end();
//          ++it) {
//         if (*it == t) {
//             container.erase(it);
//             return true;
//         }
//     }
//     return false;
// }


class Point
{
public:
    Point() : xx(0), yy(0) {}
    Point(double x, double y) : xx(x), yy(y) {}

    const double &x() const { return xx; }
    double &x() { return xx; }

    const double &y() const { return yy; }
    double &y() { return yy; }
    Point operator+(const Point &other) const { return Point(xx + other.xx, yy + other.yy); }
    Point operator-(const Point &other) const { return Point(xx - other.xx, yy - other.yy); }
    Point &operator+=(const Point &other) { xx += other.xx; yy += other.yy; return *this; }
    Point &operator-=(const Point &other) { xx -= other.xx; yy -= other.yy; return *this; }

    bool operator==(const Point &other) const { return xx == other.xx && yy == other.yy; }
    bool operator!=(const Point &other) const { return xx != other.xx || yy != other.yy; }

private:
    double xx, yy;
};

class Size
{
public:
    Size() : w(0), h(0) {}
    Size(double ww, double hh) : w(ww), h(hh) {}

    const double &width() const { return w; }
    double &width() { return w; }

    const double &height() const { return h; }
    double &height() { return h; }

    Size operator+(const Size &other) const { return Size(w + other.w, h + other.h); }
    Size operator-(const Size &other) const { return Size(w - other.w, h - other.h); }
    Size &operator+=(const Size &other) { w += other.w; h += other.h; return *this; }
    Size &operator-=(const Size &other) { w -= other.w; h -= other.h; return *this; }

    bool operator==(const Size &other) const { return w == other.w && h == other.h; }
    bool operator!=(const Size &other) const { return w != other.w || h != other.h; }

    bool isNull() const { return (w == 0 && h == 0); }
private:
    double w, h;
};

typedef void (*Setter)(void *, double dbl);
typedef double (*Getter)(void *);
class Ref
{
public:
    Ref(void *t, Getter get, Setter set) : target(t), getter(get), setter(set) {}
    Ref(const Ref &other) : target(other.target), getter(other.getter), setter(other.setter) {}
    Ref &operator=(const Ref &other) { target = other.target; getter = other.getter; setter = other.setter; return *this; }
    operator double() const { return getter(target); }
    Ref &operator=(double dbl) { setter(target, dbl); return *this; }
    Ref &operator+=(double dbl) { setter(target, getter(target) + dbl); return *this; }
    Ref &operator-=(double dbl) { setter(target, getter(target) - dbl); return *this; }
private:
    void *target;
    Getter getter;
    Setter setter;
};


class Rectangle
{
public:
    Rectangle() {}
    Rectangle(const Point &p, const Size &s) : pos(p), siz(s) {}
    Rectangle(double x, double y, double w, double h) : pos(x, y), siz(w, h) {}
    const Point &position() const { return pos; }
    Point &position() { return pos; }

    const Point &topLeft() const { return pos; }
    Point &topLeft() { return pos; }

    const Point topRight() const { return pos + Point(siz.width(), 0); }
    const Point bottomRight() const { return pos + Point(siz.width(), siz.height()); }
    const Point bottomLeft() const { return pos + Point(0, siz.height()); }
    const Point center() const { return pos + Point(siz.width() / 2.0, siz.height() / 2.0); }

    const double &width() const { return siz.width(); }
    const double &height() const { return siz.height(); }
    double &width() { return siz.width(); }
    double &height() { return siz.height(); }

    const double &left() const { return pos.x(); }
    const double &top() const { return pos.y(); }
    double &left() { return pos.x(); }
    double &top() { return pos.y(); }

    const double &x() const { return pos.x(); }
    const double &y() const { return pos.y(); }
    double &x() { return pos.x(); }
    double &y() { return pos.y(); }

    const double &right() const { return topRight().x(); }
    const double &bottom() const { return bottomRight().y(); }
    Ref right() { return Ref(this, rightGetter, rightSetter); }
    Ref bottom() { return Ref(this, bottomGetter, bottomSetter); }

    Rectangle &moveBy(const Point &p) { pos += p; return *this; };
    bool contains(const Point &p) { const Point br = bottomRight(); return p.x() >= pos.x() && p.x() < br.y() && p.y() >= pos.y() && p.y() < br.y(); }
    bool intersects(const Rectangle &other) const { return !(left() > other.right() || right() < other.left() || top() > other.bottom() || bottom() < other.top()); }
    const Size &size() const { return siz; }
    Size &size() { return siz; }

    bool operator==(const Rectangle &other) const { return pos == other.pos && siz == other.siz; }
    bool operator!=(const Rectangle &other) const { return pos != other.pos || siz != other.siz; }

    const Rectangle operator|(const Rectangle &other) const
    {
        const Point p(min(pos.x(), other.pos.x()), min(pos.y(), other.pos.y()));
        Point br = bottomRight();
        const Point otherbr = other.bottomRight();
        br.x() = min(br.x(), otherbr.x());
        br.y() = min(br.y(), otherbr.y());
        return Rectangle(p, Size(br.x() - p.x(), br.y() - p.y()));
    }

    Rectangle &operator|=(const Rectangle &other)
    {
        *this = (*this|other);
        return *this;
    }
    // need to deal with non-normalized rects
    static bool selfTest()
    {
#define TEST(cond) { if (!cond) { fprintf(stderr, "%s fails\n", #cond); goto error; } }
        Rectangle r(1, 1, 20, 20);
        TEST(r.contains(Point(10, 10)));
        TEST(!r.contains(Point(100, 10)));
        r.left() -= 1;
        TEST(r.left() == 0);
        r.top() -= 1;
        TEST(r.top() == 0);
        r.bottom() += 1;
        TEST(r.bottom() == 21);
        r.right() += 1;
        TEST(r.right() == 21);
        TEST(r.intersects(r));
#undef TEST
        fprintf(stdout, "All tests pass\n");
        return true;
    error:
#undef TEST
        return false;
    }

private:
    static void rightSetter(void *rect, double val) { Rectangle *r = static_cast<Rectangle*>(rect); r->width() += (val - r->right()); }
    static void bottomSetter(void *rect, double val) { Rectangle *r = static_cast<Rectangle*>(rect); r->height() += (val - r->bottom()); }
    static double rightGetter(void *rect) { Rectangle *r = static_cast<Rectangle*>(rect); return r->pos.x() + r->siz.width(); }
    static double bottomGetter(void *rect) { Rectangle *r = static_cast<Rectangle*>(rect); return r->pos.y() + r->siz.height(); }

    Point pos;
    Size siz;
};

}; // namespace
#endif
